//----------------------------------------------
// Multi-processing Server using fork()
// ------------------------
// Author : Woo-Hyun, Kim
// Date : Feb. 12, 2004
// Email : woorung@empal.com
//
// (Attention)
// This will be able to use on Windows and Linux.
// But Windows don't support the fork().
// So here, this will work just as an iterative server on Windows.             
//----------------------------------------------
#include <iostream>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//----------------------------------------------
#ifdef _WIN32  // on Windows
#include <winsock2.h>
#else  // on Linux
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#endif
//----------------------------------------------
// for half-close like shutdown() on Windows
#ifdef _WIN32
#define SHUT_RD SD_RECEIVE
#define SHUT_WR SD_SEND
#define SHUT_RDWR SD_BOTH
#endif
//----------------------------------------------
#define WAIT_QUEUE 10
#define SRV_PORT "2020"
//----------------------------------------------
using namespace std;
//-----------------------------------------------
void child_proc(int clnt_sock,struct sockaddr_in *pclnt_addr)
{
	cout << "connected from " << inet_ntoa(pclnt_addr->sin_addr) << endl;

	// write your code related to send() and recv()
}
//----------------------------------------------
#ifndef _WIN32
void child_exit(int sig)
{
	pid_t pid;
	int status;

	pid = waitpid(-1,&status,WNOHANG);
	if(WEXITSTATUS(status)!=0) {
		cout << "child process[" << pid << "] : abnormal exit code = " << WEXITSTATUS(status) << endl;
	}
	else cout << "child process[" << pid << "] : succesful exit code = " << WEXITSTATUS(status) << endl;
}
#endif
//----------------------------------------------
int main(int argc,char *argv[])
{
	int srv_sock;
	int clnt_sock;
	struct sockaddr_in srv_addr;
	struct sockaddr_in clnt_addr;

#ifdef _WIN32
	WSADATA wsaData;
	
	// for linking ws2_32.lib on Windows
	if(WSAStartup(0x0202,&wsaData)!=0) {
		cout << "WSAStartup() error" << endl;
		exit(-1);
	}
#else	
	pid_t pid;
	
	// for handling when a child process exits
	struct sigaction exit_sa;
	exit_sa.sa_handler = /*SIG_IGN*/ child_exit;
	sigemptyset(&exit_sa.sa_mask);
	exit_sa.sa_flags = 0;

	// register SIGCHLD signal to handle the exit for child process
	if(sigaction(SIGCHLD,&exit_sa,0)!=0) {
		cout << "sigaction() error" << endl;
		exit(-1);
	}
#endif

	srv_sock = socket(AF_INET,SOCK_STREAM,0);
	if(srv_sock<0) { 
		cout << "socket() error" << endl;
#ifdef _WIN32
		// for releasing ws2_32.lib on Windows
		WSACleanup();
#endif
		exit(-1);
	}

	memset(&srv_addr,0,sizeof(srv_addr));
	srv_addr.sin_family = AF_INET;
	srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	srv_addr.sin_port = htons(atoi(SRV_PORT));

	if(bind(srv_sock,(struct sockaddr *)&srv_addr,sizeof(srv_addr))<0) {
		cout << "bind() error" << endl;
#ifdef _WIN32
		closesocket(srv_sock);
		// for releasing ws2_32.lib on Windows
		WSACleanup();
#else
		close(srv_sock);
#endif
		exit(-1);
	}

	if(listen(srv_sock,WAIT_QUEUE)<0) {
		cout << "listen() error" << endl;
#ifdef _WIN32
		closesocket(srv_sock);
		// for releasing ws2_32.lib on Windows
		WSACleanup();
#else
		close(srv_sock);
#endif
		exit(-1);
	}

	socklen_t clnt_addr_size = sizeof(clnt_addr);
	
	while(1) {
		clnt_sock=accept(srv_sock,(struct sockaddr *)&clnt_addr,&clnt_addr_size);
		if(clnt_sock<0) continue;

#ifdef _WIN32
		// write your code~!!
		child_proc(clnt_sock,&clnt_addr);

		closesocket(clnt_sock);
#else
		if((pid=fork())<0) {
			cout << "fork() error" << endl;
			close(clnt_sock);
			close(srv_sock);
			exit(-1);
		}
		else if(pid==0) {  // in case of child process
			close(srv_sock);
	
			// write your code~!!
			child_proc(clnt_sock,&clnt_addr);

			close(clnt_sock);
			exit(0);
		}
		else {  // in case of parent process
			close(clnt_sock);			
		}
#endif
	}	
#ifdef _WIN32
	closesocket(srv_sock);
	// for releasing ws2_32.lib on Windows
	WSACleanup();
#else
	close(srv_sock);
#endif
	return 0;
}
